From: kfraser@localhost.localdomain Date: Tue, 18 Sep 2007 15:09:19 +0000 (+0100) Subject: IRQ injection changes for HVM PCI passthru. X-Git-Tag: archive/raspbian/4.8.0-1+rpi1~1^2~14937^2~71 X-Git-Url: https://dgit.raspbian.org/%22http:/www.example.com/cgi/%22https://%22%22/%22http:/www.example.com/cgi/%22https:/%22%22?a=commitdiff_plain;h=7b3cb5e541601bc0d41bc92b53451bbed11a35a8;p=xen.git IRQ injection changes for HVM PCI passthru. Signed-off-by: Allen Kay Signed-off-by: Guy Zana --- diff --git a/xen/arch/x86/hvm/irq.c b/xen/arch/x86/hvm/irq.c index b41295a273..60b89764db 100644 --- a/xen/arch/x86/hvm/irq.c +++ b/xen/arch/x86/hvm/irq.c @@ -26,7 +26,7 @@ #include #include -static void __hvm_pci_intx_assert( +void __hvm_pci_intx_assert( struct domain *d, unsigned int device, unsigned int intx) { struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq; @@ -59,7 +59,7 @@ void hvm_pci_intx_assert( spin_unlock(&d->arch.hvm_domain.irq_lock); } -static void __hvm_pci_intx_deassert( +void __hvm_pci_intx_deassert( struct domain *d, unsigned int device, unsigned int intx) { struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq; diff --git a/xen/arch/x86/hvm/vioapic.c b/xen/arch/x86/hvm/vioapic.c index 6f708b5a0c..c6faba3490 100644 --- a/xen/arch/x86/hvm/vioapic.c +++ b/xen/arch/x86/hvm/vioapic.c @@ -458,6 +458,10 @@ void vioapic_update_EOI(struct domain *d, int vector) ent = &vioapic->redirtbl[gsi]; ent->fields.remote_irr = 0; + + if ( vtd_enabled ) + hvm_dpci_eoi(gsi, ent); + if ( (ent->fields.trig_mode == VIOAPIC_LEVEL_TRIG) && !ent->fields.mask && hvm_irq->gsi_assert_count[gsi] ) diff --git a/xen/arch/x86/hvm/vmx/Makefile b/xen/arch/x86/hvm/vmx/Makefile index ba31793c2f..a7d51d4675 100644 --- a/xen/arch/x86/hvm/vmx/Makefile +++ b/xen/arch/x86/hvm/vmx/Makefile @@ -1,3 +1,5 @@ +subdir-y += vtd + subdir-$(x86_32) += x86_32 subdir-$(x86_64) += x86_64 diff --git a/xen/arch/x86/hvm/vmx/intr.c b/xen/arch/x86/hvm/vmx/intr.c index 352b9111ea..1fdad07c3d 100644 --- a/xen/arch/x86/hvm/vmx/intr.c +++ b/xen/arch/x86/hvm/vmx/intr.c @@ -138,6 +138,23 @@ static void update_tpr_threshold( __vmwrite(TPR_THRESHOLD, threshold); } +static void vmx_dirq_assist(struct domain *d) +{ + unsigned int irq; + uint32_t device, intx; + struct hvm_irq *hvm_irq = &d->arch.hvm_domain.irq; + + for ( irq = find_first_bit(hvm_irq->dirq_mask, NR_IRQS); + irq < NR_IRQS; + irq = find_next_bit(hvm_irq->dirq_mask, NR_IRQS, irq + 1) ) + { + test_and_clear_bit(irq, &hvm_irq->dirq_mask); + device = hvm_irq->mirq[irq].device; + intx = hvm_irq->mirq[irq].intx; + hvm_pci_intx_assert(d, device, intx); + } +} + asmlinkage void vmx_intr_assist(void) { int intr_vector; @@ -147,6 +164,10 @@ asmlinkage void vmx_intr_assist(void) /* Crank the handle on interrupt state. */ pt_update_irq(v); + + if ( vtd_enabled && (v->vcpu_id == 0) ) + vmx_dirq_assist(v->domain); + hvm_set_callback_irq_level(); do { diff --git a/xen/arch/x86/hvm/vmx/vtd/dmar.c b/xen/arch/x86/hvm/vmx/vtd/dmar.c index 59e59543e1..784af11818 100644 --- a/xen/arch/x86/hvm/vmx/vtd/dmar.c +++ b/xen/arch/x86/hvm/vmx/vtd/dmar.c @@ -30,6 +30,10 @@ #include "pci-direct.h" #include "pci_regs.h" +#define VTDPREFIX +int vtd_enabled; +boolean_param("vtd", vtd_enabled); + #undef PREFIX #define PREFIX VTDPREFIX "ACPI DMAR:" #define DEBUG @@ -484,11 +488,19 @@ acpi_parse_dmar(unsigned long phys_addr, unsigned long size) int acpi_dmar_init(void) { + extern int ioapic_ack_new; + acpi_table_parse(ACPI_DMAR, acpi_parse_dmar); + if (list_empty(&acpi_drhd_units)) { printk(KERN_ERR PREFIX "No DMAR devices found\n"); + vtd_enabled = 0; return -ENODEV; - } else - vtd_enabled = 1; + } + + /* Use fake-vector style of IOAPIC acknowledgement. */ + if (vtd_enabled) + ioapic_ack_new = 0; + return 0; } diff --git a/xen/arch/x86/hvm/vmx/vtd/intel-iommu.c b/xen/arch/x86/hvm/vmx/vtd/intel-iommu.c index 5af112bfe2..3146490f01 100644 --- a/xen/arch/x86/hvm/vmx/vtd/intel-iommu.c +++ b/xen/arch/x86/hvm/vmx/vtd/intel-iommu.c @@ -34,10 +34,16 @@ #include "pci_regs.h" #include "msi.h" +#define VTDPREFIX +static inline int request_irq(int vector, void *func, + int flags, char *name, void *data) +{ + return -ENOSYS; +} + extern void print_iommu_regs(struct acpi_drhd_unit *drhd); extern void print_vtd_entries(struct domain *d, int bus, int devfn, unsigned long gmfn); -extern void (*interrupt[])(void); #define DMAR_OPERATION_TIMEOUT (HZ*60) /* 1m */ @@ -831,7 +837,6 @@ static struct hw_interrupt_type dma_msi_type = { int iommu_set_interrupt(struct iommu *iommu) { int vector, ret; - unsigned long flags; vector = assign_irq_vector(AUTO_ASSIGN); vector_to_iommu[vector] = iommu; @@ -845,10 +850,7 @@ int iommu_set_interrupt(struct iommu *iommu) return -EINVAL; } - spin_lock_irqsave(&irq_desc[vector].lock, flags); irq_desc[vector].handler = &dma_msi_type; - spin_unlock_irqrestore(&irq_desc[vector].lock, flags); - set_intr_gate(vector, interrupt[vector]); ret = request_irq(vector, iommu_page_fault, 0, "dmar", iommu); if (ret) gdprintk(XENLOG_ERR VTDPREFIX, "IOMMU: can't request irq\n"); diff --git a/xen/arch/x86/hvm/vmx/vtd/io.c b/xen/arch/x86/hvm/vmx/vtd/io.c index 851c54dcdf..40d57dfd98 100644 --- a/xen/arch/x86/hvm/vmx/vtd/io.c +++ b/xen/arch/x86/hvm/vmx/vtd/io.c @@ -50,33 +50,31 @@ int hvm_do_IRQ_dpci(struct domain *d, unsigned int mirq) uint32_t link, isa_irq; struct hvm_irq *hvm_irq; - if (!vtd_enabled || (d == dom0)) + if ( !vtd_enabled || (d == dom0) || + !d->arch.hvm_domain.irq.mirq[mirq].valid ) return 0; - if (d->arch.hvm_domain.irq.mirq[mirq].valid) - { - device = d->arch.hvm_domain.irq.mirq[mirq].device; - intx = d->arch.hvm_domain.irq.mirq[mirq].intx; - link = hvm_pci_intx_link(device, intx); - hvm_irq = &d->arch.hvm_domain.irq; - isa_irq = hvm_irq->pci_link.route[link]; + device = d->arch.hvm_domain.irq.mirq[mirq].device; + intx = d->arch.hvm_domain.irq.mirq[mirq].intx; + link = hvm_pci_intx_link(device, intx); + hvm_irq = &d->arch.hvm_domain.irq; + isa_irq = hvm_irq->pci_link.route[link]; - if ( !d->arch.hvm_domain.irq.girq[isa_irq].valid ) - { - d->arch.hvm_domain.irq.girq[isa_irq].valid = 1; - d->arch.hvm_domain.irq.girq[isa_irq].device = device; - d->arch.hvm_domain.irq.girq[isa_irq].intx = intx; - d->arch.hvm_domain.irq.girq[isa_irq].machine_gsi = mirq; - } + if ( !d->arch.hvm_domain.irq.girq[isa_irq].valid ) + { + d->arch.hvm_domain.irq.girq[isa_irq].valid = 1; + d->arch.hvm_domain.irq.girq[isa_irq].device = device; + d->arch.hvm_domain.irq.girq[isa_irq].intx = intx; + d->arch.hvm_domain.irq.girq[isa_irq].machine_gsi = mirq; + } - if ( !test_and_set_bit(mirq, d->arch.hvm_domain.irq.dirq_mask) ) - { - vcpu_kick(d->vcpu[0]); - return 1; - } - else - dprintk(XENLOG_INFO, "Want to pending mirq, but failed\n"); + if ( !test_and_set_bit(mirq, d->arch.hvm_domain.irq.dirq_mask) ) + { + vcpu_kick(d->vcpu[0]); + return 1; } + + dprintk(XENLOG_INFO, "mirq already pending\n"); return 0; } @@ -86,18 +84,21 @@ void hvm_dpci_eoi(unsigned int guest_gsi, union vioapic_redir_entry *ent) uint32_t device, intx, machine_gsi; irq_desc_t *desc; - if (d->arch.hvm_domain.irq.girq[guest_gsi].valid) + ASSERT(spin_is_locked(&d->arch.hvm_domain.irq_lock)); + + if ( !vtd_enabled || !d->arch.hvm_domain.irq.girq[guest_gsi].valid ) + return; + + device = d->arch.hvm_domain.irq.girq[guest_gsi].device; + intx = d->arch.hvm_domain.irq.girq[guest_gsi].intx; + machine_gsi = d->arch.hvm_domain.irq.girq[guest_gsi].machine_gsi; + gdprintk(XENLOG_INFO, "hvm_dpci_eoi:: device %x intx %x\n", + device, intx); + __hvm_pci_intx_deassert(d, device, intx); + if ( (ent == NULL) || (ent->fields.mask == 0) ) { - device = d->arch.hvm_domain.irq.girq[guest_gsi].device; - intx = d->arch.hvm_domain.irq.girq[guest_gsi].intx; - machine_gsi = d->arch.hvm_domain.irq.girq[guest_gsi].machine_gsi; - gdprintk(XENLOG_INFO, "hvm_dpci_eoi:: device %x intx %x\n", - device, intx); - hvm_pci_intx_deassert(d, device, intx); - if ( (ent == NULL) || (ent && ent->fields.mask == 0) ) { - desc = &irq_desc[irq_to_vector(machine_gsi)]; - desc->handler->end(irq_to_vector(machine_gsi)); - } + desc = &irq_desc[irq_to_vector(machine_gsi)]; + desc->handler->end(irq_to_vector(machine_gsi)); } } @@ -107,14 +108,13 @@ int release_devices(struct domain *d) uint32_t i; int ret = 0; - if (!vtd_enabled) + if ( !vtd_enabled ) return ret; - /* unbind irq */ - for (i = 0; i < NR_IRQS; i++) { - if (hd->irq.mirq[i].valid) + for ( i = 0; i < NR_IRQS; i++ ) + if ( hd->irq.mirq[i].valid ) ret = pirq_guest_unbind(d, i); - } + iommu_domain_teardown(d); return ret; } diff --git a/xen/arch/x86/hvm/vmx/vtd/msi.h b/xen/arch/x86/hvm/vmx/vtd/msi.h index f8256607fb..cf3d1fe110 100644 --- a/xen/arch/x86/hvm/vmx/vtd/msi.h +++ b/xen/arch/x86/hvm/vmx/vtd/msi.h @@ -17,7 +17,6 @@ #define NR_HP_RESERVED_VECTORS 20 extern int vector_irq[NR_VECTORS]; -extern void (*interrupt[NR_IRQS])(void); extern int pci_vector_resources(int last, int nr_released); /* diff --git a/xen/arch/x86/hvm/vpic.c b/xen/arch/x86/hvm/vpic.c index 46ab18fc46..1ac08320fa 100644 --- a/xen/arch/x86/hvm/vpic.c +++ b/xen/arch/x86/hvm/vpic.c @@ -182,8 +182,7 @@ static void vpic_ioport_write( vpic_lock(vpic); - addr &= 1; - if ( addr == 0 ) + if ( (addr & 1) == 0 ) { if ( val & 0x10 ) { @@ -250,6 +249,11 @@ static void vpic_ioport_write( vpic->isr &= ~(1 << irq); if ( cmd == 7 ) vpic->priority_add = (irq + 1) & 7; + if ( vtd_enabled ) + { + irq |= ((addr & 0xa0) == 0xa0) ? 8 : 0; + hvm_dpci_eoi(hvm_isa_irq_to_gsi(irq), NULL); + } break; case 6: /* Set Priority */ vpic->priority_add = (val + 1) & 7; diff --git a/xen/arch/x86/io_apic.c b/xen/arch/x86/io_apic.c index 6eba445b45..7b399850ce 100644 --- a/xen/arch/x86/io_apic.c +++ b/xen/arch/x86/io_apic.c @@ -184,6 +184,68 @@ static void __modify_IO_APIC_irq (unsigned int irq, unsigned long enable, unsign } } +static int real_vector[MAX_IRQ_SOURCES]; +static int fake_vector=-1; + +/* + * Following 2 functions are used to workaround spurious interrupt + * problem related to mask/unmask of interrupts. Instead we program + * an unused vector in the IOAPIC before issueing EOI to LAPIC. + */ +static void write_fake_IO_APIC_vector (unsigned int irq) +{ + struct irq_pin_list *entry = irq_2_pin + irq; + unsigned int pin, reg; + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + for (;;) { + pin = entry->pin; + if (pin == -1) + break; + reg = io_apic_read(entry->apic, 0x10 + pin*2); + real_vector[irq] = reg & 0xff; + reg &= ~0xff; + + if (fake_vector == -1) + fake_vector = assign_irq_vector(MAX_IRQ_SOURCES-1); + + reg |= fake_vector; + io_apic_write(entry->apic, 0x10 + pin*2, reg); + + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } + spin_unlock_irqrestore(&ioapic_lock, flags); +} + +static void restore_real_IO_APIC_vector (unsigned int irq) +{ + struct irq_pin_list *entry = irq_2_pin + irq; + unsigned int pin, reg; + unsigned long flags; + + spin_lock_irqsave(&ioapic_lock, flags); + for (;;) { + pin = entry->pin; + if (pin == -1) + break; + + reg = io_apic_read(entry->apic, 0x10 + pin*2); + reg &= ~0xff; + reg |= real_vector[irq]; + io_apic_write(entry->apic, 0x10 + pin*2, reg); + mb(); + *(IO_APIC_BASE(entry->apic) + 0x10) = reg & 0xff; + + if (!entry->next) + break; + entry = irq_2_pin + entry->next; + } + spin_unlock_irqrestore(&ioapic_lock, flags); +} + /* mask = 1 */ static void __mask_IO_APIC_irq (unsigned int irq) { @@ -1356,7 +1418,11 @@ static void mask_and_ack_level_ioapic_irq (unsigned int irq) if ( ioapic_ack_new ) return; - mask_IO_APIC_irq(irq); + if ( vtd_enabled ) + write_fake_IO_APIC_vector(irq); + else + mask_IO_APIC_irq(irq); + /* * It appears there is an erratum which affects at least version 0x11 * of I/O APIC (that's the 82093AA and cores integrated into various @@ -1398,8 +1464,12 @@ static void end_level_ioapic_irq (unsigned int irq) if ( !ioapic_ack_new ) { - if ( !(irq_desc[IO_APIC_VECTOR(irq)].status & IRQ_DISABLED) ) - unmask_IO_APIC_irq(irq); + if ( !(irq_desc[IO_APIC_VECTOR(irq)].status & IRQ_DISABLED) ) { + if ( vtd_enabled ) + restore_real_IO_APIC_vector(irq); + else + unmask_IO_APIC_irq(irq); + } return; } diff --git a/xen/include/asm-x86/hvm/irq.h b/xen/include/asm-x86/hvm/irq.h index e0a9faddbf..142aeadd41 100644 --- a/xen/include/asm-x86/hvm/irq.h +++ b/xen/include/asm-x86/hvm/irq.h @@ -114,8 +114,12 @@ struct hvm_irq { #define hvm_isa_irq_to_gsi(isa_irq) ((isa_irq) ? : 2) /* Modify state of a PCI INTx wire. */ +void __hvm_pci_intx_assert( + struct domain *d, unsigned int device, unsigned int intx); void hvm_pci_intx_assert( struct domain *d, unsigned int device, unsigned int intx); +void __hvm_pci_intx_deassert( + struct domain *d, unsigned int device, unsigned int intx); void hvm_pci_intx_deassert( struct domain *d, unsigned int device, unsigned int intx); diff --git a/xen/include/xen/irq.h b/xen/include/xen/irq.h index ca55235519..b92f785b94 100644 --- a/xen/include/xen/irq.h +++ b/xen/include/xen/irq.h @@ -64,9 +64,6 @@ extern irq_desc_t irq_desc[NR_IRQS]; extern int setup_irq(unsigned int, struct irqaction *); extern void free_irq(unsigned int); -extern int request_irq(unsigned int irq, - void (*handler)(int, void *, struct cpu_user_regs *), - unsigned long irqflags, const char * devname, void *dev_id); extern hw_irq_controller no_irq_type; extern void no_action(int cpl, void *dev_id, struct cpu_user_regs *regs); @@ -80,11 +77,11 @@ extern int pirq_guest_unbind(struct domain *d, int irq); static inline void set_native_irq_info(int irq, cpumask_t mask) { - irq_desc[irq].affinity = mask; + irq_desc[irq].affinity = mask; } static inline void set_irq_info(int irq, cpumask_t mask) { - set_native_irq_info(irq, mask); + set_native_irq_info(irq, mask); } #endif /* __XEN_IRQ_H__ */